<?php

use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;

function preg_split_end(string $pattern, string $subject): string
{
    preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE);

    if (!empty($matches[0])) {
        $lastMatch = end($matches[0]);
        $endPos = $lastMatch[1] + strlen($lastMatch[0]);
        return substr($subject, 0, $endPos);
    }
    return $subject;
}

function youhua(string $hanshu, string $cont, int $count = 150): string
{
    if (!preg_match('/' . preg_quote($hanshu, '/') . '/ims', $cont)) {
        return $cont;
    }

    $pattern = '/\$\\w{6}=\'.{0,' . $count . '}\';' . preg_quote($hanshu, '/') . ' \$\\w{6};/ims';
    if (!preg_match_all($pattern, $cont, $matches) || empty($matches[0])) {
        return $cont;
    }

    $replacements = [];
    foreach ($matches[0] as $str) {
        if (preg_match('/\$\\w{6}=(\'.{0,' . $count . '}\';)' . preg_quote($hanshu, '/') . ' \$\\w{6};/ims', $str, $match)) {
            $replacements[$str] = $hanshu . ' ' . $match[1];
        }
    }

    return str_replace(array_keys($replacements), array_values($replacements), $cont);
}

function hanshu(string $data): string
{
    if (!preg_match_all('/call_user_func_array\("(\w+)",array\((.*?)\)\)/ims', $data, $matches)) {
        return $data;
    }

    $replacements = [];
    foreach ($matches[0] as $key => $fullMatch) {
        $funcName = $matches[1][$key];
        $params = preg_replace('/&/', '', $matches[2][$key]);
        $replacements[$fullMatch] = $funcName . '(' . $params . ')';
    }

    return str_replace(array_keys($replacements), array_values($replacements), $data);
}

function bin_h(string $data): string
{
    return preg_replace('/\$\w+=\&?\$GLOBALS\[\w+\]\[0?\];/ims', '', $data);
}

function array_is(string $data): string
{
    $patterns = [
        '/if\(is_array\(\$\w+\[\w+\]\)\)goto \w{0,15};goto \w{0,15};\w{0,15}:goto \w{0,10};\w{0,10}:\w{0,10}:/ims',
        '/if\(is_array\(\$\w+\[\w+\]\)\)goto \w{0,20};goto \w{0,20};\w{0,20}:/ims'
    ];
    return preg_replace($patterns, '', $data);
}

function safe_eval(string $code)
{
    try {
        eval($code);
        return isset($string) ? $string : null;
    } catch (Throwable $e) {
        return null;
    }
}

function md_5(string $data): string
{
    return preg_replace_callback('/(&\$GLOBALS\[\w+\]\[\w+\])/ims', function ($matches) {
        $result = safe_eval('<?php $string = ' . $matches[0] . ';');
        return is_string($result) ? "'" . addslashes($result) . "'" : $matches[0];
    }, $data);
}

function packs(string $data): string
{
    return preg_replace_callback('/pack\(\'H\*\',\'\w+\'\)/ims', function ($matches) {
        $result = safe_eval('<?php $string = ' . $matches[0] . ';');
        return is_string($result) ? "'" . addslashes($result) . "'" : $matches[0];
    }, $data);
}

function echo_conf(string $cont): string
{
    return preg_replace(
        '/\$\w+=\'\w+\';echo ?\$conf\[\$\w+\]/ims',
        'echo $conf[\'$1\']',
        $cont
    );
}

function remove_goto_patterns(string $data, array $patterns): string
{
    foreach ($patterns as $pattern) {
        $data = preg_replace($pattern, '', $data);
    }
    return $data;
}

function process_goto_structures(string $data): string
{
    $gotoPatterns = [
        '/echo \'.{0,100}\';(goto \w+;\w+:\w+:goto \w+;\w+:\w+:)echo \'.{0,100}\'[$;]/ims',
        '/echo \'.{0,100}\';(goto \w+;\w+:\w+:goto \w+;\w+:\w+:)echo \$conf\[\'.{0,100}\'\][$;]/ims',
        '/echo \$conf\[\'.{0,100}\'\][$;](goto \w+;\w+:\w+:goto \w+;\w+:\w+:)echo \'.{0,100}\';/ims',
        '/echo \'.{0,100}\';(goto \w+;\w+:\w+:)echo \'.{0,100}\'[$;](goto \w+;\w+:\w+:)/ims'
    ];

    return remove_goto_patterns($data, $gotoPatterns);
}

function format_code(string $content): string
{
    try {
        $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
        $ast = $parser->parse($content);
        return (new PrettyPrinter\Standard)->prettyPrintFile($ast);
    } catch (Error $e) {
        error_log("Parse error: {$e->getMessage()}");
        return $content;
    }
}

// Main execution
if ($argc < 2) {
    exit("Usage: php decrypt.php <filename>\n");
}

$file = $argv[1];
if (!file_exists($file)) {
    exit("File not found: $file\n");
}

$content = file_get_contents($file);
if (strpos($content, '文件已解密一次') !== false) {
    exit("文件已解密过\n");
}

try {
    $topbin = preg_split_end('/\$GLOBALS\[.+?\]=explode\(.+?\);/i', $content);
    $shujv = preg_split('/unset\(\$\w+\);/ims', $content);

    $processed = [];
    foreach ($shujv as $fenduan) {
        if (preg_match('/call_user_func_array\("pack",array\(&\$(\w+),&\$(\w+)\)\);/', $fenduan, $m)) {
            $varName = $m[1];
            $result = safe_eval('<?php $str = pack(\'H*\',$' . $varName . ');');
            if ($result) {
                $fenduan = preg_replace([
                    '/\\$' . $varName . '\s*=\s*.+?;/',
                    '/call_user_func_array\("pack",array\(&\$\w+,&\$\w+\)\)/'
                ], ['', "'" . addslashes($result) . "'"], $fenduan);
            }
        }
        $processed[] = $fenduan;
    }

    $cont = implode('', $processed);
    $cont = youhua('echo', $cont);
    $cont = hanshu($cont);
    $cont = bin_h($cont);
    $cont = array_is($cont);
    $cont = md_5($cont);
    $cont = packs($cont);
    $cont = echo_conf($cont);
    $cont = process_goto_structures($cont);

    // $formatted = format_code($cont);
    $outputFile = pathinfo($file, PATHINFO_FILENAME) . '-decode.' . pathinfo($file, PATHINFO_EXTENSION);

    file_put_contents($outputFile, $cont . "\n//文件已解密一次");
    echo "解密成功，输出文件: $outputFile\n";
} catch (Throwable $e) {
    exit("处理过程中发生错误: " . $e->getMessage() . "\n");
}
